/*
 * UsimCardService contains methods for the communication with UICC with USIM an application.
 * UsimCardService extends JSmexCardService and implements all its abstract methods.
 *
 *  This file is part of JSmex.
 *
 *  JSmex is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  JSmex is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Foobar; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

package jsmex.cardservice;

import java.util.Vector;
import jsmex.function.FileProperties;
import jsmex.function.SmartCardFile;
import opencard.core.terminal.CommandAPDU;
import opencard.core.terminal.ResponseAPDU;
import opencard.core.terminal.CardTerminalException;
import opencard.core.service.CardServiceScheduler;
import opencard.core.service.SmartCard;
import opencard.core.service.CardServiceException;
import opencard.core.util.HexString;
import jsmex.JSmexTools;


/**
 * @author Tobias Senger
 *
 */


public class UsimCardService extends JSmexCardService{
    
    private static CommandAPDU command = new CommandAPDU(50);
    private static ResponseAPDU response;
    private static ResponseAPDU lastresponse;
    private static byte[] selectedFID = new byte[2];
    private boolean chv1Verified = false;
    private boolean chv2Verified = false;
    private boolean pinEnabled = false;
    private boolean usimAppSelected = false;
    byte[] USIM_AID = { (byte)0xA0, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x87, (byte)0x10, (byte)0x02, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x89, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00};
    
    public UsimCardService() {
        super();
    }
    
    
    /** This method returns the FID of the currently selected EF or DF.
     *
     * @return An byte-array which contains the FID of the currently selected EF or DF.
     */
    public byte[] getSelectedFID() {
        byte[] status_cmd = { (byte)0xA0, (byte)0xF2, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06 };;
        command.setLength(0);
        command.append(status_cmd);
        try {
            response = sendCommandAPDU(command);
            selectedFID[0] = (byte)response.getByte(4);
            selectedFID[1] = (byte)response.getByte(5);
            lt.info("called method getSelectedFID(). returns: "+HexString.hexify(selectedFID),this);
            
        } catch (CardTerminalException cte) {
            lt.warning("method getSelectedFID() throws a CardTerminalException.",this);
        }
        return selectedFID;
    }
    
    /**This method returns an array of SmartCardFile which contains all founded
     * files under the DF which ist specified in parameter scfile.
     *
     * @param scfile Contains a SmartCardFile instance of a DF.
     * @return An array of SmartCardFile.
     */
    public SmartCardFile[] getFileList(SmartCardFile scfile) {
        
        lt.info("called method getFileList(SmartCardFile scfile). scfile name is"+scfile.toString()+". scfile FID is"+HexString.hexify(scfile.fp.getFID()), this);
        
        if (scfile.fp.getFID()[0]==(byte)0x3F && scfile.fp.getFID()[1]==(byte)0x00) {                 //---------MF--------------
            String[][] mffiles = {{"EF DIR","2F 00"},{"EF ICCID","2F E2"},{"EF ELP","2F 05"},{"EF ARR","2F 06"},{"DF GSM","7F 20"},{"DF TELECOM","7F 10"}, {"ADF USIM","FF FF"}};
            
            try {
                selectMF();
            } catch (Exception exc) {}
            
            return buildFileList(mffiles);
        }
        
        else if (scfile.fp.getFID()[0]==(byte)0x7F && scfile.fp.getFID()[1]==(byte)0x10) {            //---------DF TELECOM--------------
            String[][] dftelecomfiles = {{"EF ADN","6F 3A"},{"EF FDN","6F 3B"},{"EF SMS","6F 3C"},{"EF CCP","6F 3D"},{"EF MSISDN","6F 40"},{"EF SMSP","6F 42"},{"EF SMSS","6F 43"}
            ,{"EF LND","6F 44"},{"EF EXT1","6F 4A"},{"EF EXT2","6F 4B"},{"EF SDN","6F 49"}};
            
            try {
                selectFID("7F 10");
            } catch (Exception exc) {}
            
            return buildFileList(dftelecomfiles);
        }
        
        else if (scfile.fp.getFID()[0]==(byte)0x7F && scfile.fp.getFID()[1]==(byte)0x20) {            //---------DF GSM--------------
            String[][] dfgsmfiles = {{"EF LP","6F 05"},{"EF IMSI","6F 07"},{"EF KC","6F 20"},{"EF PLMsel","6F 30"},{"EF HPLMN","6F 31"},{"EF ACMmax","6F 37"},{"EF SST","6F 38"},{"EF ACM","6F 39"},{"EF GID1","6F 3E"}
            ,{"EF GID2","6F 3F"},{"EF PUCT","6F 41"},{"EF CBMI","6F 45"},{"EF SPN","6F 46"},{"EF BCCH","6F 74"},{"EF ACC","6F 78"},{"EF FPLMN","6F 7B"},{"EF LOCI","6F 7E"},{"EF AD","6F AD"},{"EF PHASE","6F AE"}
            ,{"DF Graphics","5F 50"}};
            
            try {
                selectFID("7F 20");
            } catch (Exception exc) {}
            
            return buildFileList(dfgsmfiles);
        } else if (scfile.fp.getFID()[0]==(byte)0x5F && scfile.fp.getFID()[1]==(byte)0x50) {            //---------DF GSM.DF Graphics--------------
            String[][] dfgsmfiles = {{"EF IMG","4F 20"},{"EF IMGDataX","4F 21"}};
            
            try {
                selectFID("5F 50");
            } catch (Exception exc) {}
            
            return buildFileList(dfgsmfiles);
        }
        
        else if (java.util.Arrays.equals(scfile.fp.getAID(), USIM_AID)) {            //---------DF USIM--------------
            String[][] usimfiles = {{"EF IMSI","6F 07"},{"EF Keys","6F 08"},{"EF KeysPS","6F 09"},{"EF HPLMN","6F 31"},{"EF UST","6F 38"},{"EF ACC","6F 78"},{"EF FPLMN","6F 7B"}
            ,{"EF LOCI","6F 7E"},{"EF AD","6F AD"},{"EF ECC","6F B7"},{"EF PSLOCI","6F 73"},{"EF START-HFN","6F 7B"},{"EF THRESHOLD","6F 5C"}
            ,{"EF ARR","6F 06"},{"DF PHONEBOOK","5F 3A"},{"DF GSM-ACCESS","5F 3B"},{"DF MExE","5F 3C"}};
            try {
                selectAID(USIM_AID);
            } catch (Exception exc) {}
            
            return buildFileList(usimfiles);
        }
        
        else if (scfile.fp.getFID()[0]==(byte)0x5F && scfile.fp.getFID()[1]==(byte)0x3A) {            //---------DF USIM.DF PHONEBOOK--------------
            String[][] phfiles = {{"EF DIR","2F 00"}};
            try {
                selectFID("5F 3A");
            } catch (Exception exc) {}
            
            return buildFileList(phfiles);
        }
        
        else if (scfile.fp.getFID()[0]==(byte)0x5F && scfile.fp.getFID()[1]==(byte)0x3B) {            //---------DF USIM.DF GSM-ACCESS--------------
            String[][] gafiles = {{"EF DIR","2F 00"}};
            try {
                selectFID("5F 3B");
            } catch (Exception exc) {}
            
            return buildFileList(gafiles);
        }
        
        else if (scfile.fp.getFID()[0]==(byte)0x5F && scfile.fp.getFID()[1]==(byte)0x3C) {            //---------DF USIM.DF MExE--------------
            String[][] phfiles = {{"EF DIR","2F 00"}};
            try {
                selectFID("5F 3C");
            } catch (Exception exc) {}
            
            return buildFileList(phfiles);
        }
        
        else return null;
    }
    
    /** This method returns true if the PIN is verified.
     *
     * @param chvindicator This argument contaisn the number of the PIN (1 or 2).
     * @return Returns true if the PIN is verified. Otherwise false.
     */
    public boolean chvIsVerified(int chvindicator) {
        lt.info("called method chvIsVerified("+Integer.toString(chvindicator)+").",this);
        if (chvindicator==1) return chv1Verified;
        if (chvindicator==2) return chv2Verified;
        return false;
    }
    
    /**
     * This function selects a file (DF or EF) on the card.
     * @param fid The FID to select in an byte-array.
     * @return ResponseAPDU of the command.
     */
    public ResponseAPDU selectFID(byte[] fid) {
        return selectFID(fid, (byte)0x00, (byte)0x04);
    }
    
    /**
     * This function selects a file (DF or EF)
     * @param fid file ID to select
     * @return ResponseAPDU
     */
    public ResponseAPDU selectFID(byte[] fid, byte p1, byte p2) {
        byte[] select_cmd= {(byte)0x00, (byte)0xA4, p1, p2, (byte)0x02};
        command.setLength(0);
        command.append(select_cmd);
        command.append(fid);
        command.append((byte)0x00);
        try {
            response = sendCommandAPDU(command);
            if (response.sw() == 0x9000)  {
                selectedFID[0] = (byte)response.getByte(4);
                selectedFID[1] = (byte)response.getByte(5);
            }
            
        } catch (CardTerminalException cte) {
            lt.warning("method selectFID() throws a CardTerminalException",this);
        }
        lt.info("called method selectFID( FID:"+HexString.hexify(fid)+" ). returns SW: "+HexString.hexify(response.sw1())+" "+HexString.hexify(response.sw2()),this);
        return response;
    }
    
    /**
     * This function selects a file (DF or EF)
     * @param fid The FID to select in a String.
     * @return ResponseAPDU of the command.
     */
    public ResponseAPDU selectFID(String fid) {
        lt.info("called method selectFID("+fid+").",this);
        return selectFID(JSmexTools.parseHexString(fid));
    }
    
    /** This method implements the abstract method from JSmexCardService.
     *  It select the a EF whose FID comes in parameter fid.
     *
     * @param fid The FID to be selected in an byte-array.
     * @return Returns the ReponseAPDU of the select commando.
     */
    public ResponseAPDU selectEF(byte[] fid) {
        lt.info("called method selectEF("+HexString.hexify(fid)+")",this);
        return selectFID(fid);
    }
    
    /** This method implements the abstract method from JSmexCardService.
     *  It select the a DF whose FID comes in parameter fid.
     *
     * @param fid The FID to be selected in an byte-array.
     * @return Returns the ReponseAPDU of the select commando.
     */
    public ResponseAPDU selectDF(byte[] fid) {
        lt.info("called method selectDF("+HexString.hexify(fid)+")",this);
        if (fid[0]==(byte)0xFF && fid[1]==(byte)0xFF) selectAID(USIM_AID); //little trick becaus ExplorerFunctions doesn't know AID's
        else return selectFID(fid);
        return null;
    }
    
    /**
     * This method selects the parent DF on the card.
     * @return Returns the ResponseAPDU from this command.
     */
    private ResponseAPDU selectParentDF() {
        byte[] select_cmd= {(byte)0x00, (byte)0xA4, (byte)0x03, (byte)0x0C, (byte)0x00};
        command.setLength(0);
        command.append(select_cmd);
        try {
            response = sendCommandAPDU(command);
            
            if (response.sw() == 0x9000) {
                selectedFID[0] = (byte)response.getByte(4);
                selectedFID[1] = (byte)response.getByte(5);
            }
            
        } catch (CardTerminalException cte) {
            lt.warning("method selectParentDF() throws a CardTerminalException.",this);
        }
        lt.info("called method selectParentDF(). returns SW: "+HexString.hexify(response.sw1())+" "+HexString.hexify(response.sw2()),this);
        return response;
    }
    
    /** This method implements the abstract method from JSmexCardService.
     *  It selects the MF of the card.
     *
     * @return Returns 'true' if everything went well,
     * otherwise 'false' will be returned.
     */
    public boolean selectMF() { // TODO überarbeiten
        if (selectFID("3F 00").sw()==0x9000) {
            lt.info("called method selectMF(). return is \"true\"", this);
            return true;
        } else {
            lt.info("called method selectMF(). return is \"false\"", this);
            return false;
        }
    }
    
    /**
     * Reads the current selected tranparent EF
     * @param oh offset high
     * @param ol offset low
     * @param length length of data to read
     * @return ResponseAPDU
     */
    public ResponseAPDU readBinary(byte oh, byte ol, byte length) {
        byte[] rb_cmd = { (byte)0x00, (byte)0xB0, (byte)oh, (byte)ol, (byte)length};
        command.setLength(0);
        command.append(rb_cmd);
        try {
            response = sendCommandAPDU(command);
        } catch (CardTerminalException cte) {
            lt.warning("method readBinary(oh: "+HexString.hexify(oh)+", ol: "+HexString.hexify(ol)+", length: "+HexString.hexify(length)+
                    ") throws CardTerminalExecption.",this);
        }
        lt.info("called method readBinary(oh: "+HexString.hexify(oh)+", ol: "+HexString.hexify(ol)+", length: "+HexString.hexify(length)+
                ")."+" returns SW: "+HexString.hexify(response.sw1())+" "+HexString.hexify(response.sw2()),this);
        return response;
    }
    
    /**
     * Updates the current selected transparent EF
     * @param oh offset high
     * @param ol offset low
     * @param data Data to write
     * @return ResponseAPDU
     */
    
    public ResponseAPDU updateBinary(byte oh, byte ol, byte[] data) {
        byte[] ub_cmd = { (byte)0xA0, (byte)0xD6, (byte)oh, (byte)ol, (byte)data.length};
        command.setLength(0);
        command.append(ub_cmd);
        command.append(data);
        
        try {
            response = sendCommandAPDU(command);
        } catch (CardTerminalException cte) {
            lt.warning("method updateBinary(byte oh, byte ol, byte[] data) throws CardTerminalExecption.",this);
        }
        lt.info("called method updateBinary(byte oh, byte ol, byte[] data)."+" returns SW: "+HexString.hexify(response.sw1())+" "+HexString.hexify(response.sw2()),this);
        return response;
    }
    
    /**This method checks if the EFs and DFs which are get in parameter filearray.
     * If the file exist, an new instance of SmartCardFile will builded.
     * All founded SmartCardFiles will be returned in an array.
     *
     * @param filearray A two-dimensional array which contains FIDs and names of EFs and DFs.
     * @return An array of SmartCardFile.
     */
    private SmartCardFile[] buildFileList(String[][] filearray) {
        
        ResponseAPDU resp = null;
        SmartCardFile[] scfiles = null;
        Vector filevec = new Vector();
        
        lt.info("called method buildFileList(String[][] filearray)", this);
        
        for (int i=0; i<filearray.length;i++){
            
            if (filearray[i][0]=="ADF USIM") {
                byte[] fid = JSmexTools.parseHexString(filearray[i][1]);
                FileProperties properties = new FileProperties(0, 0x38, 0, 0, fid, (byte)0x00, (byte)0x0f);
                properties.setAID(USIM_AID);
                SmartCardFile tempfile = new SmartCardFile(filearray[i][0], properties);
                filevec.add(tempfile); // und wird ein neues File erzeugt und in einem Vektor abgelegt.
            }
            
            else {
                try {
                    resp = selectFID(filearray[i][1]);    //erstmal testen ob es das DF auch wirklich gibt...
                    if (resp.sw1()==(byte)0x90) { // Wenn ja die eigenschaften herausfinden
                        FileProperties properties = getProperties(resp);
                        SmartCardFile tempfile = new SmartCardFile(filearray[i][0], properties);
                        filevec.add(tempfile); // und wird ein neues File erzeugt und in einem Vektor abgelegt.
                    }
                } catch (Exception exc) {System.out.println(exc);}
            }
        }
        
        scfiles = new SmartCardFile[filevec.size()];  // Länge für das Array aus Vectorgröße ermittel
        scfiles =  (SmartCardFile[]) filevec.toArray(scfiles); // Inhalt des Vectors in ein Array umwandeln
        return scfiles;
    }
    
    
    /**
     * This method selects the application with the in parameter aid given AID.
     * @param aid This is an byte-array with the FID to select.
     * @return Returns 'true' is selection was successful. Otherwise 'false' will be returned.
     */
    private boolean selectAID(byte[] aid) {
        byte[] select_cmd= {(byte)0x00, (byte)0xA4, (byte)0x04, (byte)0x00, (byte)aid.length};
        
        command.setLength(0);
        command.append(select_cmd);
        command.append(aid);
        try {
            response = sendCommandAPDU(command);
            if (response.sw() != 0x9000) {
                lt.warning("AID "+ HexString.hexify(aid) +" not found",this);
                return false;
            }
        }
        
        catch (CardTerminalException cte) {
            lt.warning("method: selectFID() CardTerminalException: "+cte.toString(),this);
            return false;
        }
        lt.info("called method selectAID(aid:"+HexString.hexify(aid)+" ). returns SW: "+HexString.hexify(response.sw1())+" "+HexString.hexify(response.sw2()),this);
        if (java.util.Arrays.equals(aid , USIM_AID)) usimAppSelected = true;
        return true;
    }
    
    /**This method implements the abstract method from JSmexCardService.
     * It reads one complete record of the currently selected linear or cyclic EF.
     *
     * @return Returns a ResponseAPDU.
     */
    public ResponseAPDU readRecord(byte recno, int length) {
        byte recordLength = (byte) length;
        byte[] rr_cmd = { (byte)0x00, (byte)0xB2, (byte)recno, (byte)0x04, (byte)recordLength};
        command.setLength(0);
        command.append(rr_cmd);
        try {
            response = sendCommandAPDU(command);
        } catch (CardTerminalException cte) {
            lt.warning("method readRecord(recno: "+HexString.hexify(recno)+", length: "+Integer.toString(length)+") throws an CardTerminalException.",this);
        }
        lt.info("called method readRecord(recno:"+HexString.hexify(recno)+", length:"+Integer.toString(length)+")",this);
        return response;
    }
    
    
    
    /**
     * This function reads one complete record in the current linear fixed or cyclic EF.
     * @param recno record number (absolute mode only)
     * @param mode Define the read mode: <ul>
     *          <li>0x02: next record</li>
     * 		<li>0x03: previous record</li>
     * 		<li>0x04: absolute mode, record number is given in recNo;</li>
     *          </ul>
     * @param recordLength The record length of the record.
     * @return The ResponseAPDU
     */
    public ResponseAPDU readRecord(byte recno, byte mode, byte recordLength) {
        byte[] rr_cmd = { (byte)0x00, (byte)0xB2, (byte)recno, (byte)mode, (byte)recordLength};
        command.setLength(0);
        command.append(rr_cmd);
        try {
            response = sendCommandAPDU(command);
        } catch (CardTerminalException cte) {
            lt.warning("method readRecord(recno:"+HexString.hexify(recno)+", mode: "+HexString.hexify(mode)+", recordLength:"+Integer.toString(recordLength)+") throws a CardTerminalException.",this);
        }
        lt.info("called method readRecord(recno:"+HexString.hexify(recno)+", mode: "+HexString.hexify(mode)+", recordLength:"+Integer.toString(recordLength)+")",this);
        return response;
    }
    
    /**
     * This function updates one complete record in the current linear fixed or cyclic EF.
     * @param recno record number (absolute mode only)
     * @param mode Parameter mode definies the update mode:
     *  <ul>
     *      <li>'02' = next record</li>
     *      <li>'03' = previous record</li>
     *      <li>'04' absolute mode record number, is given in recNo</li>
     *  </ul>
     * @param data The data to write into the record.
     * @return ResponseAPDU
     */
    public ResponseAPDU updateRecord(byte recno, byte mode, byte[] data) {
        byte[] ur_cmd = { (byte)0xA0, (byte)0xDC, (byte)recno, (byte)mode, (byte)data.length};
        command.setLength(0);
        command.append(ur_cmd);
        command.append(data);
        try {
            response = sendCommandAPDU(command);
        } catch (CardTerminalException cte) {
            lt.warning("method updateRecord(byte recno, byte mode, byte[] data) throws a CardTerminalException.",this);
        }
        lt.info("method updateRecord(byte recno, byte mode, byte[] data). returns SW: "+HexString.hexify(response.sw1())+" "+HexString.hexify(response.sw2()),this);
        return response;
    }
    
    
    
    /**
     * This function verifies the CHV
     * @param chvindication CHV indication (1 or 2)
     * @param chv This parameter contains the CHV to verify in a String.
     * @return ResponseAPDU
     */
    public ResponseAPDU verifyCHV(byte chvindication, String chv){
        byte[] verify_cmd = {(byte)0x00, (byte)0x20, (byte)0x00, (byte)chvindication, (byte)0x08};
        command.setLength(0);
        command.append(verify_cmd);
        command.append(JSmexTools.stringToPin(chv));
        
        try {
            response = sendCommandAPDU(command);
        } catch (CardTerminalException cte) {
            lt.warning("method verifyCHV(byte chvindication, String chv) throws an CardTerminalException.",this);
        }
        if (response.sw()==0x9000) {
            if (chvindication==(byte)0x01) chv1Verified = true;
            if (chvindication==(byte)0x02) chv2Verified = true;
        }
        lt.info("called method verifyCHV(chvindication: "+HexString.hexify(chvindication)+", chv: "+chv+"). returns SW: "+HexString.hexify(response.sw1())+" "+HexString.hexify(response.sw2()),this);
        return response;
        
    }
    
    
    
    /**This method search in the ResponseAPDU data for read access condition an return it in a byte.
     *
     * @param resp This parameter contains the ResponseAPDU from a read command on an EF_ARR.
     * @return This method returns the readAC in a byte.
     */
    private byte getReadAC(ResponseAPDU resp) {
        
        byte [] data = resp.data();
        lt.info("called method getReadAC(ResponseAPDU resp)",this);
        
        for (int i=0;i<data.length;i++) {
            if (data[i]==(byte)0x80 && data[i+1]==(byte)0x01 && (data[i+2]&0x01)==0x01) {
                if (data[i+3]==(byte)0x90) return (byte)0x00;
                else if (data[i+3]==(byte)0x97) return (byte)0x0F;
                for (int j=i;j<data.length;j++) {
                    if (data[j]==(byte)0x83 && data[j+1]==(byte)0x01 && data[j+3]==(byte)0x95) {
                        if (data[j+2]==0x01) return (byte)0x01; // CHV1
                        else if (data[j+2]==0x02) return (byte)0x02; // CHV2
                        else return (byte)0xFF; // anything else
                    }
                }
            }
        }
        return (byte) 0xFF; //anything else
    }
    
    /**This method search in the ResponseAPDU data for update access condition an return it in a byte.
     *
     * @param resp This parameter contains the ResponseAPDU from a read command on an EF_ARR.
     * @return This method returns the updateAC in a byte.
     */
    private byte getUpdateAC(ResponseAPDU resp) {
        
        byte [] data = resp.data();
        lt.info("called method getUpdateAC(ResponseAPDU resp)",this);
        
        for (int i=0;i<data.length;i++) {
            if (data[i]==(byte)0x80 && data[i+1]==(byte)0x01 && (data[i+2]&0x02)==0x02) {
                if (data[i+3]==0x90) return (byte)0x00;
                else if (data[i+3]==0x97) return (byte)0x0F;
                for (int j=i;j<data.length;j++) {
                    if (data[j]==(byte)0x83 && data[j+1]==(byte)0x01 && data[j+3]==(byte)0x95) {
                        if (data[j+2]==0x01) return (byte)0x01; // CHV1
                        else if (data[j+2]==0x02) return (byte)0x02; // CHV2
                        else return (byte)0xFF; // anything else
                    }
                }
            }
        }
        return (byte) 0xFF; //anything else
    }
    
    /** This method reads out the properties from the ResponseAPDU from a selectFID() command.
     *
     * @param resp The ResponseAPDU from a selectFID() command
     * @return Returns an instance of FileProperties
     */
    private FileProperties getProperties(ResponseAPDU resp) {
        
        int size = 0;
        int df_descriptor = 0;
        int tof = 0;
        int recordlength = 0; //length of one record
        int records = 0; //number of record
        byte[] file_id = new byte[2];
        byte[] fid_ef_rule = new byte[2];
        byte sfi = 0;
        byte readac = 0;
        byte updateac = 0;
        
        lt.info("called method getProperties(ResponseAPDU resp)", this);
        
        byte[] data = resp.data();
        
        if (data[0]==(byte)0x62) {   // search FCP tag 62
            
            int p = 2;  // p is a pointer
            
            do {
                switch (data[p]) {
                    
                    case (byte)0x82: {  // file descriptor tag 0x82
                        tof = data[p+2]&(byte)0x3F;
                        if (tof!=0x38) tof = data[p+2]&(byte)0x07;
                        if (data[p+1]==0x05) {
                            recordlength = JSmexTools.toUnsignedInt(data[p+4])*256+JSmexTools.toUnsignedInt(data[p+5]);
                            records = JSmexTools.toUnsignedInt(data[p+6]);
                        }
                        p+=data[p+1]+2;
                        break;
                    }
                    
                    case (byte)0x83: {  // file_id tag 0x83
                        file_id[0] = data[p+2];
                        file_id[1] = data[p+3];
                        p+=data[p+1]+2;;
                        break;
                    }
                    
                    case (byte)0xA5: {  // proprietary info tag 0xA5
                        p+=data[p+1]+2;
                        break;
                    }
                    
                    case (byte)0x8A: {  // Life cycle state tag 0x8a
                        p+=data[p+1]+2;
                        break;
                    }
                    
                    case (byte)0x8B: {
                        byte[] tempSelectedFid = selectedFID;
                        byte[] tempDfFid = null;
                        byte[] ef_arr_fid = new byte[2];
                        ef_arr_fid[0] = data[p+2];
                        ef_arr_fid[1] = data[p+3];
                        
                        if (tof==0x38) {  // If DF
                            tempDfFid = selectedFID;
                            selectParentDF();
                        }
                        
                        ResponseAPDU tempresp = selectFID(ef_arr_fid);
                        tempresp = readRecord(data[p+5],(byte)0x04,(byte)0);
                        readac = getReadAC(tempresp);
                        updateac = getUpdateAC(tempresp);
                        
                        if (tof==0x38) selectFID(tempDfFid);
                        
                        selectFID(tempSelectedFid);
                        p+=data[p+1]+2;
                        break;
                    }
                    
                    case (byte)0x80: {  // file size tag;
                        for (int i=0;i<data[p+1];i++) {
                            int x,y = 0;
                            x = JSmexTools.toUnsignedInt(data[p+2+i]);
                            y = (int)Math.pow(256,(int)(data[p+1]-i-1));
                            size += (x*y);
                        }
                        p+=data[p+1]+2;
                        break;
                    }
                    
                    case (byte)0x81: {
                        p+=data[p+1]+2;
                        break;
                    }
                    
                    case (byte)0x88: {
                        p+=data[p+1]+2;
                        break;
                    }
                    
                    case (byte)0xC6: {
                        p+=data[p+1]+2;
                        break;
                    }
                    
                    default : {
                        p++;
                        break;
                    }
                }
                
            } while (p<data.length);
        }
        
        FileProperties fp = new FileProperties(size, tof, recordlength, records, file_id, readac, updateac);
        return fp;
        
    }
    
    /** This method implements the method of JSmexCardService.
     *  It checks if there is an EF or DF with the fid, which comes in fidString,
     *  the currently selected DF. If a file was found, the method returns a new SmartCardFile
     *  instance for the founded file. Else it return null.
     *
     * @param fidString a String which contains the FID
     * @return a instance of SmartCardFile or null
     */
    public SmartCardFile getSmartCardFile(String fidString) {
        
        byte[] fid = JSmexTools.parseHexString(fidString);
        
        ResponseAPDU resp = selectDF(fid);
        String name;
        
        if (resp.sw()!=0x9000) {
            resp = selectEF(fid);
            if (resp.sw()!=0x9000) {
                lt.info("called method getSmartCardFile(fidString: "+fidString+"). returns: 'null'",this);
                return null;
            }
            
        }
        
        FileProperties fp = getProperties(resp);
        
        if (fp.isDF()) name = new String("DF ("+fidString+")");
        else name = new String("EF ("+fidString+")");
        
        SmartCardFile scfile = new SmartCardFile(name ,fp);
        lt.info("called method getSmartCardFile(fidString: "+fidString+"). returns new SmartCardFile with the name: "+name ,this);
        return scfile;
    }
    
    /**This method implements the method of JSmexCardService.
     * It updates one complete record in the current linear or cyclic EF.
     * @param recno This is the record number to update.
     * @param data This parameter contains the data to write.
     * @return This method return the ResponseAPDU from this command.
     */
    public ResponseAPDU updateRecord(byte recno, byte[] data) {
        lt.info("called method updateRecord(byte recno, byte[] data)",this);
        return updateRecord(recno, (byte)0x04, data);
    }
    
}

